/*
 *	$Id: gui.cpp 163 2009-06-20 00:05:27Z wabsie $
 */

#include "gui.h" // Hvis andre #include's i denne fil end denne, så slet dem


const int	gui::guiFlags = AG_VIDEO_RESIZABLE;
const char*	gui::guiTitle = "FieldVIZ - by Mark Ruvald & Jesper Marrup";
const int	gui::guiFPS = 30;

gui::gui(int w, int h, int bpp)
{
	// Initialisér Agar
	if (AG_InitCore(guiTitle, 0) == -1)
		die("%s\n", AG_GetError());
	if (AG_InitVideo(w, h, bpp, guiFlags) == -1)
		die("%s\n", AG_GetError());

	// Vis 30 frames/sec. Agars default er -1, dvs (forsøgt) uendeligt hurtigt
	AG_SetRefreshRate(guiFPS);

	// Tryk på [Esc]-knap => Afslut
	AG_BindGlobalKey(SDLK_ESCAPE, KMOD_NONE, AG_Quit); // kalder AG_Quit()

	// Tilføj menu-linje, fælles for alle viz-vinduer
	appMenu = AG_MenuNewGlobal(0);

	AG_MenuItem	*mFile = AG_MenuNode(appMenu->root, "File", NULL);
	{
		AG_MenuAction(mFile, "New", agIconDoc.s, menuNewVIZ, "%p", this);
		AG_MenuAction(mFile, "Open", agIconLoad.s, menuOpen, "%p", this);
		AG_MenuAction(mFile, "Save", agIconSave.s, menuSave, "%p", this);
		AG_MenuAction(mFile, "Close", agIconTrash.s, menuClose, "%p", this);
		AG_MenuSeparator(mFile);
		AG_MenuAction(mFile, "Quit", agIconClose.s, (AG_EventFn)AG_Quit, NULL);
	}

	AG_MenuDynamicItem(appMenu->root, "Window list", NULL, menuWin, "%p", this);
    AG_MenuDynamicItem(appMenu->root, "Add/remove objects", NULL, menuObj, "%p", this);
    AG_MenuDynamicItem(appMenu->root, "About", NULL, menuAbout, "%p", this);

	return;
}

gui::~gui() {
	AG_Destroy();	// Frigør Agar-allokerede resourcer
}

void gui::run() {
	AG_EventLoop();	// Håndtér brugerinput, kald callbacks om nødvendigt
	return;			// Returnerer først når vinduet lukkes
}

void gui::die(const char *format, ...) const
{
	va_list arg;

	va_start(arg, format);
	vfprintf(stderr, format, arg);
	va_end(arg);

	exit(EXIT_FAILURE);
}

int gui::findActiveViz()
{
	/* AG_Window'ers Z-order gemmes i en linked list (AG_TAILQ* macro'erne).
	 * Det aktive/fokuserede AG_Window er altid angivet som sidste element i
	 * den linkede list - det er det vindue vi søger. Men når man klikker på
	 * menulinjen og skal til enten at lukke eller gemme det nuværende
	 * fokuserede vindue, fjernes fokus jo fra vinduet hen på menulinjen.
	 * Derved er bliver menulinjen det sidste element i den linkede list, og
	 * vores søgte AG_Window det næst-sidste. Denne orden kan dog forstyrres 
	 * af AG_TextInfo-bokse. */
	AG_Window *foo;
	stack<AG_Window*> tmpwins;

	this->buryTheDead();

	// Loop åbne viz-vinduer igennem, og husk dem i deres Z-orden
	AG_TAILQ_FOREACH(foo, &agView->windows, windows) {
		if (strstr(foo->caption, ".viz")) // NB: Titlen skal være filnavnet!
			tmpwins.push(foo);
	}

	if (tmpwins.empty())
		return -1;			// Ingen vinduer åbne

	foo = tmpwins.top();	// Find det øverste af dem

	for (int i = 0; i < this->win.size(); i++)
		if (this->win[i]->isWinEqual(foo))
			return i;

	return -1;				// Gør compileren glad
}

void gui::loadVIZ(AG_Event *event)
{	/* Agar call convention: file-chosen (char *path, AG_FileType *type) */
	gui *This = (gui*)AG_PTR(1);
	char *path = AG_STRING(2);

	ifstream fil(path);
	if (!fil.is_open()) {
		AG_TextTmsg(AG_MSG_ERROR, 2500, "Unable to open file\n'%s'", path);
		return;
	}

	vector<string> linjer;
	while (!fil.eof()) {
		string linje;
		getline(fil, linje);
		linjer.push_back(linje);
	}

	fil.close();
	
	char *tmp;
	char *titel = "Unknown.viz";
	if ((tmp = strrchr(path, '/')) != NULL)
		titel = tmp+1;
	if ((tmp = strrchr(path, '\\')) != NULL)
		titel = tmp+1;

	vizobj *nyt = new vizobj(titel);
	parser load(nyt);
	load.text2VIZ(linjer);
	nyt->plotwrapper();
	This->win.push_back(nyt);

	return;
}

void gui::menuOpen(AG_Event *event)
{
	gui *This = (gui*)AG_PTR(1);

	AG_Window *win;
	AG_FileDlg *fd;
	AG_FileType *ft;

	win = AG_WindowNew(0);
	AG_WindowSetCaption(win, "Load existing file");
	fd = AG_FileDlgNew(win, AG_FILEDLG_EXPAND | AG_FILEDLG_LOAD | AG_FILEDLG_CLOSEWIN);
	ft = AG_FileDlgAddType(fd, "Visual Inspection Zone file", "*.viz", loadVIZ, "%p", This);

	AG_WindowShow(win);
	return;
}

void gui::menuSave(AG_Event *event)
{
	gui *This = (gui*)AG_PTR(1);

	int index = This->findActiveViz();
	if (index < 0) {
		AG_TextInfo("Save-key", "The window can not be saved since none are open!");
		return;
	}

	char *path = This->win[index]->getName();	// Vi gemmer bare i cwd
	ofstream fil(path);
	if (!fil.is_open())
	{
		AG_TextTmsg(AG_MSG_ERROR, 2500, "Could not save file\n'%s'", path);
		return;
	}

	for (int i = 0; i < This->win[index]->vem.size(); i++)
	{
		fil << This->win[index]->vem[i]->getInfo("%.4fE%02.0f") << "\n";
	}
	fil.close();

	AG_TextTmsg(AG_MSG_INFO, 1500, "Saved file\n'%s'", path);
	return;
}

void gui::menuClose(AG_Event *event)
{
	gui *This = (gui*)AG_PTR(1);

	int index = This->findActiveViz();
	if (index < 0) {
		AG_TextInfo("close-key", "The window can not be closed since none are open!");
		return;
	}

//	printf("The active window '%s' was closed\n", This->win[index]->getName());
	This->win[index]->close();
	This->buryTheDead();

	return;
}

void gui::menuNewVIZ(AG_Event *event)
{
	/*
	 * Vi vil gerne have en non-static klasse metode som callback, men det
	 * må vi ikke pga. Agar's callback konvention antager globale funktioner.
	 * En static metode ligger i selve klassen og ikke i objektinstansen af
	 * klassen; altså er en static metode entydigt global. -Men vi mister
	 * netop adgang til objekt instansen i static metoder :(
	 *
	 * Workaround: Vi kan pass'e this pointeren(=objektinstansen) som
	 * argument til vores static callback :)
	 */
	gui *This = (gui*)AG_PTR(1);

	static int cnt = 0;
	char navn[FILENAME_MAX];
	snprintf(navn, sizeof(navn), "New field configuration %i.viz", ++cnt);

	vizobj *temp = new vizobj(navn); // Opret nyt vizobj og kald default constructor
	This->win.push_back(temp);

	return;
}

void gui::menuObj(AG_Event *event)
{
	gui *This = (gui*)AG_PTR(1);
	AG_MenuItem *m = (AG_MenuItem*)AG_SENDER();

    int index = This->findActiveViz();
	if (index < 0) {
//		AG_TextInfo("object-key", "No VIZ windows open, so you can't create/delete any objects!");
		return;
	}

	vizobj *aktivt = This->win[index];

	for (int i = 0; i < aktivt->vem.size(); i++) {
		char buf[BUFSIZ] = "";
		snprintf(buf, sizeof(buf), "%i %s", i+1, aktivt->vem[i]->getInfo().c_str());
		AG_MenuAction(m, buf, agIconTrash.s, menuObjDelete, "%p,%p,%i", This, aktivt, i);
	}

	AG_MenuSeparator(m);
	AG_MenuAction(m, "Add point charge", agIconGear.s, menuObjAdd, "%p,%p,%i", This, aktivt, emobj::pcharge);
	AG_MenuAction(m, "Add line charge", agIconGear.s, menuObjAdd, "%p,%p,%i", This, aktivt, emobj::lcharge);
	AG_MenuAction(m, "Add line current", agIconGear.s, menuObjAdd, "%p,%p,%i", This, aktivt, emobj::lcurrent);

	return;
}

void gui::menuObjDelete(AG_Event *event)
{
	/*
	 * Callback som sletter det ønskede objekt, og opdaterer plottet
	 */

	gui *This = (gui*)AG_PTR(1);
	vizobj *aktivt = (vizobj*)AG_PTR(2);
	int i = AG_INT(3);

//	printf("Slettet em-objektet %s\n", aktivt->vem[i]->getInfo().c_str());
	aktivt->removeObj(i);
	aktivt->plotwrapper(); // Opdatér plottet nu hvor vi har fjernet et objekt
	return;
}

void gui::menuObjAdd(AG_Event *event)
{
	gui *This = (gui*)AG_PTR(1);
	vizobj *aktivt = (vizobj*)AG_PTR(2);
	emobj::type type = (emobj::type)AG_INT(3);

	// Tilføj ønsket objekt
	aktivt->vem.addObj(type, 1e-6, vektor<3>(0,0,0), vektor<3>(0,0,1));
	aktivt->plotwrapper(); // Opdatér plottet nu hvor vi har tilføjet et objekt

	return;
}

void gui::buryTheDead()
{
	// Fjern døde viz-vinduer
	for (int i = this->win.size()-1; i >= 0; i--) {
		if (this->win[i]->closed)
			this->win.erase(this->win.begin() + i); // Slet, og kald destructor
	}

	return;
}

void gui::menuWin(AG_Event *event)
{
	/*
	 * Kaldes én gang hver gang inden Window menuens child item vises.
	 * Lukkede viz-vinduer bliver først deallokeret her.
	 */

	gui *This = (gui*)AG_PTR(1);
	AG_MenuItem *m = (AG_MenuItem*)AG_SENDER();

	This->buryTheDead();

	// Og skriv en liste ud med de levende...
	for (int i = 0; i < This->win.size(); i++) {
		AG_MenuAction(m, This->win[i]->getName(), NULL, menuWinSelect, "%p,%i", This, i);
	}

	return;
}

void gui::menuWinSelect(AG_Event *event)
{
	/*
	 * Callback: Giver fokus til det ønskede (allerede åbne) viz-vindue.
	 */

	gui *This = (gui*)AG_PTR(1);
	int index = AG_INT(2);

	This->win[index]->giveFocus();

	return;
}

void gui::menuAbout(AG_Event *event)
{
	AG_Window *win = AG_WindowNew(	AG_WINDOW_KEEPABOVE | AG_WINDOW_NORESIZE | 
									AG_WINDOW_NOMINIMIZE| AG_WINDOW_NOMAXIMIZE);
	AG_WindowSetCaption(win, "Field Visual Inspection Zone");
	{
		AG_Label *lAbout;
		AG_Label *lInfo;

		lAbout = AG_LabelNew(win, AG_LABEL_FRAME | AG_LABEL_EXPAND,
		    "FieldVIZ is coded in C++ by\n"
			"\n"
			"  Mark Ruvald Pedersen, s072095 & \n"
			"  Jesper Marrup, s062035\n"
			"\n"
			"as part of DTU course '31015 Fagprojekt F09'.\n"
			"\n\n"
			"Documentation and source code located at:\n"
			" http://code.google.com/p/fieldviz/");
		AG_LabelJustify(lAbout, AG_TEXT_LEFT);

		// Hent og vis Agar-version and grafikdriver
		AG_AgarVersion ver;
		AG_GetVersion(&ver);
		lInfo = AG_LabelNew(win, 0,
			"\nCurrently using:\n"
		    " libAgar version %d.%d.%d (%s),\n"
		    " %s as graphics driver.",
		    ver.major, ver.minor, ver.patch, ver.release,
		    AG_GetBool(agConfig, "view.opengl") ? "OpenGL" : "SDL");
		AG_LabelJustify(lInfo, AG_TEXT_LEFT);
	}
	AG_WindowSetPosition(win, AG_WINDOW_CENTER, 0);
	AG_WindowShow(win);

	return;
}
